from PyQt4.QtCore import *
from geolib.geom2d import XY
from geolib.proj import Projections, LatLonBounds


class SceneProjection(object):
    '''A collection of functions that transforms QGraphicsScene information into LatLon information'''
    
    def latLonBounds(self, sceneRect, wrapLon=True):
        return LatLonBounds.fromPoints(self.latLonPoint(sceneRect.bottomLeft(), wrapLon), self.latLonPoint(sceneRect.topRight(), wrapLon))
    
    def sceneRect(self, latLonBounds, wrapLon=True):
        return QRectF(self.scenePoint(latLonBounds.topLeft(), wrapLon), self.scenePoint(latLonBounds.bottomRight(), wrapLon))
    
    def latLonPoint(self, scenePoint, wrapLon=True):
        '''Takes a scene QPointF and returns an XYZ object representing lat/lon'''
        raise NotImplementedError
    
    def scenePoint(self, latLonPoint, wrapLon=True):
        '''Takes an XYZ object representing lat/lon and returns a scene QPointF'''
        raise NotImplementedError
    
    def maxSceneRect(self):
        return self.sceneRect(self.maxLatLonBounds())

class XYSceneProjection(SceneProjection):
    '''Base class for projections so they don't need to worry about flipping the Y coordinate'''
    
    def __init__(self, geoprojection):
        self.__geoprojection = geoprojection
    
    def scenePoint(self, latLon, wrapLon=True):
        xyPoint = self.__geoprojection.projectPoint(latLon, wrapLon)
        return QPointF(xyPoint[0], -xyPoint[1])
    
    def latLonPoint(self, scenePt, wrapLon=True):
        xyPoint = (scenePt.x(), -scenePt.y())
        return self.__geoprojection.inverseProjectPoint(xyPoint, wrapLon)

    def maxXYBounds(self):
        '''Override this or maxLatLonBounds to define max bounds for this projection'''
        return self.__geoprojection.projectedBounds()
    
    def maxLatLonBounds(self):
        '''Override this or maxXYBounds to define max bounds for this projection'''
        return self.__geoprojection.latLonBounds()
    
    def maxSceneRect(self):
        xyBounds = self.maxXYBounds()
        latLonBounds = self.maxLatLonBounds()
        if xyBounds is None and latLonBounds is None:
            raise NotImplementedError
        
        if xyBounds is not None:
            topLeftXY = xyBounds.topLeft()
            bottomRightXY = xyBounds.bottomRight()
            return QRectF(QPointF(topLeftXY[0], -topLeftXY[1]), QPointF(bottomRightXY[0], -bottomRightXY[1]))
        else:
            return self.sceneRect(self.maxLatLonBounds())
    

class SceneProjections(object):
    
    def __init__(self):
        pass
    
    LATLON = XYSceneProjection(Projections.LATLON)
    GOOGLE_MAPS = XYSceneProjection(Projections.googlemaps())
    GOOGLE_MAPS_OPPOSITE = XYSceneProjection(Projections.googlemaps(180))
    MERCATOR = XYSceneProjection(Projections.mercator())
    MERCATOR_OPPOSITE = XYSceneProjection(Projections.mercator(180))
    EQUAL_AREA = XYSceneProjection(Projections.cylequalarea())
    EQUAL_AREA_OPPOSITE = XYSceneProjection(Projections.cylequalarea(180))
    
    @staticmethod
    def utm(utmTuple):
        return XYSceneProjection(Projections.utm(utmTuple))
    
